package producersConsumersTests.results;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import producersConsumersTests.bypassersCount.ClockVectorTask;

/**
 * Timeline class maintains the order of events in the system in three different
 * sorted lists: insertion events, removal events and all events.
 * 
 * It can count how many tasks bypassed each other using them.
 * 
 */
public class EventSupervisor {
	private final LinkedList<EntranceEvent> entries = new LinkedList<EntranceEvent>();
	private final LinkedList<ExitEvent> exits = new LinkedList<ExitEvent>();
	private final LinkedList<ContainerEvent> allEvents = new LinkedList<ContainerEvent>();
	private double average = 0;
	private long max = 0;
	private static class Checker implements Runnable {
		private final EntranceEvent entry;
		private final LinkedList<EntranceEvent> allEntries;
		private long result = 0;

		public Checker(EntranceEvent entry, LinkedList<EntranceEvent> entries) {
			super();
			this.entry = entry;
			this.allEntries = entries;
		}

		@Override
		public void run() {
			ExitEvent exit = entry.getExit();
			for (EntranceEvent anotherEntry : allEntries) {
				if (anotherEntry.greaterThan(entry)
						&& exit.greaterThan(anotherEntry.getExit()))
					result++;
			}
		}

		public long getResult() {
			return result;
		}
	}

	public EventSupervisor(Collection<ClockVectorTask> tasks) {
		for (ClockVectorTask task : tasks) {
			EntranceEvent entrance = new EntranceEvent(task.entry(), task);
			ExitEvent exit = new ExitEvent(task.exit(), task);
			entrance.setExit(exit);
			exit.setEntry(entrance);
			entries.add(entrance);
			exits.add(exit);
			allEvents.add(entrance);
			allEvents.add(exit);
		}
	}

	private static final int numThreads = 32;
	private List<Long> measures = new LinkedList<Long>();

	public double calculateAverageBypassers() throws InterruptedException {
		long sum = 0;
		ExecutorService e = Executors.newFixedThreadPool(numThreads);
		Checker checkers[] = new Checker[entries.size()];
		int i = 0;
		for (EntranceEvent entry : entries) {
			checkers[i] = new Checker(entry, entries);
			e.execute(checkers[i++]);
		}
		e.shutdown();
		e.awaitTermination(14, TimeUnit.DAYS);
		for (Checker c : checkers) {
			measures.add(c.getResult());
			if (c.getResult() > max)
				max  = c.getResult();
			sum += c.getResult();
		}
		average = (double) sum / checkers.length;
		return average;
	}

	public double getAverageBypassers() {
		return average;
	}

	public long getMaxBypassers() {
		return max;
	}
	
	public List<Long> getMeasures() {
		return this.measures;
	}
}
